package com.woniuxy.yoga.progress.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.yoga.commons.core.model.Result;
import com.woniuxy.yoga.commons.redis.util.rediskeyutil.CourseRedisKeyUtil;
import com.woniuxy.yoga.progress.client.OrderClient;
import com.woniuxy.yoga.progress.dao.model.Subcard;
import com.woniuxy.yoga.progress.dao.mapper.SubcardMapper;
import com.woniuxy.yoga.progress.model.exception.ProgressException;
import com.woniuxy.yoga.progress.model.exception.ProgressExceptionCode;
import com.woniuxy.yoga.progress.model.param.CreateSubCardParam;
import com.woniuxy.yoga.progress.service.SubcardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 次卡流程表 服务实现类
 * </p>
 *
 * @author 韦用文,刘存益,郎腾霄
 * @since 2023年11月28日
 */
@Service
public class SubcardServiceImpl extends ServiceImpl<SubcardMapper, Subcard> implements SubcardService {

    @Autowired
    private SubcardMapper subcardMapper;

    @Autowired
    private OrderClient orderClient;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 创建次卡进度
     * @param param param
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createSubCard(CreateSubCardParam param) {
        //验证是否重复添加
        QueryWrapper<Subcard> wrapper = new QueryWrapper<>();
        wrapper.eq("order_num",param.getOrderNum());
        Subcard subcard = subcardMapper.selectOne(wrapper);
        if (subcard!=null) throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_CARD_REPEAT);
        //添加进度表
        Subcard card = new Subcard();
        card.setOrderNum(param.getOrderNum());
        card.setSubcardCount(param.getOrderCount());
        card.setUserId(param.getUserId());
        card.setSubcardUse(0);
        subcardMapper.insert(card);
    }

    /**
     * 完成次卡
     * @param subcardId 次卡进度卡id
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void finishSubcard(Integer subcardId) {
        //查询卡是否存在
        Subcard subcard = subcardMapper.selectById(subcardId);
        if (subcard == null)throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_CARD_NOT_EXIST);
        //查询是否完成
        if (!Objects.equals(subcard.getSubcardCount(), subcard.getSubcardUse()))
            throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_SUB_CARD_NOT_USED);
        //修改订单状态
        Result<String> result = orderClient.completeOrder(subcard.getOrderNum());
        if (result.getCode()!=200) throw new ProgressException(result.getCode(),result.getMessage());
        //修改
        subcard.setCompleteTime(DateUtil.format(new Date(System.currentTimeMillis()),"yyyy-MM-dd HH:mm:ss"));
        subcardMapper.updateById(subcard);
    }

    /**
     * 次卡进度次数加一
     * @param subcardId 次卡id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSubCardCount(Integer subcardId) throws Exception{
        //创建单锁
        RLock lock = redissonClient.getFairLock(CourseRedisKeyUtil.lockCurrentCountByCourseId(subcardId));
        boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
        try {
            if (!isLock){
                throw new Exception("服务器繁忙请稍后再试");
            }
            //查询进度
            Subcard subcard = subcardMapper.selectById(subcardId);
            if (subcard==null) throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_CARD_NOT_EXIST);
            if (subcard.getSubcardUse()+1>subcard.getSubcardCount())
                throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_SUB_CARD_COUNT_STACK);
            if (subcard.getSubcardUse()+1==subcard.getSubcardCount())
                this.finishSubcard(subcardId);
            //次数+1
            subcard.setSubcardUse(subcard.getSubcardUse()+1);
            subcardMapper.updateById(subcard);
        }finally {
            //判断是否是自己的锁 并且还未释放
            if (lock.isLocked()){
                lock.unlock();
            }
        }
    }

    /**
     * 次卡进度次数减一
     * @param subcardId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reduceSubCardCount(Integer subcardId) throws Exception{
        //创建单锁
        RLock lock = redissonClient.getFairLock(CourseRedisKeyUtil.lockCurrentCountByCourseId(subcardId));
        boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
        try {
            if (!isLock){
                throw new Exception("服务器繁忙请稍后再试");
            }
            //查询进度
            Subcard subcard = subcardMapper.selectById(subcardId);
            if (subcard==null) throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_CARD_NOT_EXIST);
            if (subcard.getSubcardUse()==0)
                throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_SUB_CARD_COUNT_STACK);
            //次数+1
            subcard.setSubcardUse(subcard.getSubcardUse()-1);
            subcardMapper.updateById(subcard);
        }finally {
            //判断是否是自己的锁 并且还未释放
            if (lock.isLocked()){
                lock.unlock();
            }
        }

    }

    /**
     * 查询未完成次卡
     * @param userId userId
     * @return Subcard
     */
    @Override
    public List<Subcard> getUnfinishSub(Integer userId) {
        //创建查询
        QueryWrapper<Subcard> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userId);
        wrapper.isNull("complete_time");
        wrapper.orderByAsc("subcard_id");
        List<Subcard> subcards = subcardMapper.selectList(wrapper);
        if (subcards.size()==0) throw new ProgressException(ProgressExceptionCode.PROGRESS_EXCEPTION_CODE_CARD_NOT_EXIST);
        return subcards;
    }
}
